Введение в анализ данных¶
Домашнее задание 2. Простой анализ данных и ИИ-инструменты.¶
Правила, прочитайте внимательно:
- Выполненную работу нужно отправить телеграм-боту
@thetahat_ds25_bot. Для начала работы с ботом каждый раз отправляйте/start. Дождитесь подтверждения от бота, что он принял файл. Если подтверждения нет, то что-то не так. Работы, присланные иным способом, не принимаются. - Дедлайн см. в боте. После дедлайна работы не принимаются кроме случаев наличия уважительной причины.
- Прислать нужно ноутбук в формате
ipynb. Если вы строите интерактивные графики, их стоит прислать в формате html. - Следите за размером файлов. Бот не может принимать файлы весом более 20 Мб. Если файл получается больше, заранее разделите его на несколько.
- Выполнять задание необходимо полностью самостоятельно. При обнаружении списывания всем участникам списывания дается штраф -2 балла к итоговой оценке за семестр.
- Решения, размещенные на каких-либо интернет-ресурсах, не принимаются. Кроме того, публикация решения в открытом доступе может быть приравнена к предоставлении возможности списать.
- Обратите внимание на правила использования ИИ-инструментов при решении домашнего задания.
- Код из рассказанных на занятиях ноутбуков можно использовать без ограничений.
- Для выполнения задания используйте этот ноутбук в качестве основы, ничего не удаляя из него. Можно добавлять необходимое количество ячеек.
- Комментарии к решению пишите в markdown-ячейках.
- Выполнение задания (ход решения, выводы и пр.) должно быть осуществлено на русском языке.
- Решение проверяется системой ИИ-проверки
ThetaGrader. Результат проверки валидируется и исправляется человеком, после чего комментарии отправляются студентам. - Если код будет не понятен проверяющему, оценка может быть снижена.
- Никакой код из данного задания при проверке запускаться не будет. Если код студента не выполнен, недописан и т.д., то он не оценивается.
Важно!!! Правила заполнения ноутбука:
- Запрещается удалять имеющиеся в ноутбуке ячейки, менять местами положения задач.
- Сохраняйте естественный линейный порядок повествования в ноутбуке сверху-вниз.
- Отвечайте на вопросы, а также добавляйте новые ячейки в предложенных местах, которые обозначены
<...>. - В markdown-ячейка, содержащих описание задачи, находятся специальные отметки, которые запрещается модифицировать.
- При нарушении данных правил работа может получить 0 баллов.
Перед выполнением задания посмотрите презентацию по выполнению и оформлению домашних заданий с занятия 2.
Баллы за задание:
Легкая часть (достаточно на "хор"):
- Задача 1 — 5 баллов
- Задача 2 — 25 баллов
Сложная часть (необходимо на "отл"):
- Задача 3 — 80 баллов
- Задача 4 — 50 баллов
Баллы учитываются в обязательной части курса и не влияют на оценку по факультативной части.
# Bot check
# HW_ID: fpmi_ad2
# Бот проверит этот ID и предупредит, если случайно сдать что-то не то
# Status: final
# Перед отправкой в финальном решении удали "not" в строчке выше
# Так бот проверит, что ты отправляешь финальную версию, а не промежуточную
from itertools import count
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='whitegrid', palette='Set2')
На лекции мы рассмотрели, как можно использовать инструменты искусственного интеллекта в нашем курсе, чтобы повысить личную эффективность. В этом домашнем задании мы предлагаем вам две технические задачи, которые можно полностью доверить ИИ. Они относятся к легкой части задания. Бывает работа, которую делать просто не хочется: она долгая, рутинная, несодержательная, не несет какой-либо интеллектуальной ценности. Такую работу по возможности стоит спихнуть искусственному интеллекту.
Также в сложной части вы найдёте задачу по работе с библиотеками и небольшой анализ научной статьи из списка на ваш выбор. Все эти задания можно выполнить самостоятельно, но мы рекомендуем воспользоваться помощью ИИ, чтобы облегчить процесс 😊
Легкая часть¶
Задача 1.¶
Это задача для разминки всего на 5 баллов. Возможно, она покажется вам забавной, но это действительно так 😃
Ниже представлен код на языке C++. Перепишите его на Python и запустите без ошибок. Поясните, что делает этот код?
Если код не работает, обсудите проблему с моделью или используйте другую, эта задача гарантированно решается.
# #include <iostream>
# #include <vector>
# #include <cstdlib>
# #include <ctime>
#
# #define 😃 int
# #define 🎉 void
# #define 🍎 std::vector<int>
# #define 🖨️ std::cout
# #define 🔢 rand()
# #define ⏰ time(0)
# #define 🔀 srand
# #define 🔁 for
# #define 🔄 while
# #define 🎲 if
# #define 🎈 else
# #define 🚀 return
# #define 🎯 main
# #define 🖥️ std::endl
#
# 🎉 🎭(🍎& 🍏, 😃 🍌, 😃 🍍, 😃 🍇) {
# 😃 🍒 = 🍍 - 🍌 + 1;
# 😃 🍓 = 🍇 - 🍍;
#
# 🍎 🍑(🍒), 🍐(🍓);
#
# 🔁 (😃 🍔 = 0; 🍔 < 🍒; 🍔++)
# 🍑[🍔] = 🍏[🍌 + 🍔];
# 🔁 (😃 🍔 = 0; 🍔 < 🍓; 🍔++)
# 🍐[🍔] = 🍏[🍍 + 1 + 🍔];
#
# 😃 🍔 = 0, 🍕 = 0, 🍖 = 🍌;
# 🔄 (🍔 < 🍒 && 🍕 < 🍓) {
# 🎲 (🍑[🍔] <= 🍐[🍕]) {
# 🍏[🍖] = 🍑[🍔];
# 🍔++;
# } 🎈 {
# 🍏[🍖] = 🍐[🍕];
# 🍕++;
# }
# 🍖++;
# }
#
# 🔄 (🍔 < 🍒) {
# 🍏[🍖] = 🍑[🍔];
# 🍔++;
# 🍖++;
# }
#
# 🔄 (🍕 < 🍓) {
# 🍏[🍖] = 🍐[🍕];
# 🍕++;
# 🍖++;
# }
# }
#
# 🎉 🍿(🍎& 🍏, 😃 🍌, 😃 🍇) {
# 🎲 (🍌 < 🍇) {
# 😃 🍍 = 🍌 + (🍇 - 🍌) / 2;
#
# 🍿(🍏, 🍌, 🍍);
# 🍿(🍏, 🍍 + 1, 🍇);
#
# 🎭(🍏, 🍌, 🍍, 🍇);
# }
# }
#
# 🍎 🎲(😃 🎯, 😃 🎰, 😃 🎳) {
# 🍎 🎨(🎯);
# 🔁 (😃 🍔 = 0; 🍔 < 🎯; 🍔++) {
# 🎨[🍔] = 🔢 % (🎳 - 🎰 + 1) + 🎰;
# }
# 🚀 🎨;
# }
#
# 🎉 🖨️(const 🍎& 🍏) {
# 🔁 (😃 🍔 : 🍏) {
# 🖨️ << 🍔 << " ";
# }
# 🖨️ << 🖥️;
# }
#
# 😃 🎯() {
# 🔀(⏰);
#
# 😃 🎯 = 20;
# 😃 🎰 = 0;
# 😃 🎳 = 100;
#
# 🍎 🍏 = 🎲(🎯, 🎰, 🎳);
#
# 🖨️ << "?: ";
# 🖨️(🍏);
#
# 🍿(🍏, 0, 🍏.size() - 1);
#
# 🖨️ << "??: ";
# 🖨️(🍏);
#
# 🚀 0;
# }
import random
import time
def merge(arr, left, mid, right):
"""
Сливает две отсортированные части массива arr:
- arr[left..mid]
- arr[mid+1..right]
Результат сохраняется в arr[left..right].
"""
n1 = mid - left + 1
n2 = right - mid
# Создаем временные массивы для левой и правой половин
L = arr[left:left + n1]
R = arr[mid + 1:mid + 1 + n2]
i, j = 0, 0 # индексы для L и R
k = left # индекс для arr
# Сливаем, пока не достигнем конца одного из подмассивов
while i < n1 and j < n2:
if L[i] <= R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
# Копируем оставшиеся элементы из L, если они есть
while i < n1:
arr[k] = L[i]
i += 1
k += 1
# Копируем оставшиеся элементы из R, если они есть
while j < n2:
arr[k] = R[j]
j += 1
k += 1
def merge_sort(arr, left, right):
"""
Рекурсивная сортировка слиянием.
Сортирует часть массива arr с индексами от left до right.
"""
if left < right:
mid = (left + right) // 2
merge_sort(arr, left, mid)
merge_sort(arr, mid + 1, right)
merge(arr, left, mid, right)
def generate_random_vector(size, low, high):
"""
Генерирует список из size случайных целых чисел
в диапазоне [low, high].
"""
return [random.randint(low, high) for _ in range(size)]
def print_vector(arr):
"""
Выводит элементы списка arr в одну строку.
"""
print(" ".join(map(str, arr)))
def main():
# Инициализируем генератор случайных чисел
random.seed(time.time())
n = 20 # размер массива
low = 0 # нижняя граница случайных чисел
high = 100 # верхняя граница случайных чисел
arr = generate_random_vector(n, low, high)
print("?: ", end="")
print_vector(arr)
merge_sort(arr, 0, len(arr) - 1)
print("??: ", end="")
print_vector(arr)
if __name__ == "__main__":
main()
?: 84 26 62 25 79 11 53 99 91 52 33 81 56 41 40 12 10 73 8 82 ??: 8 10 11 12 25 26 33 40 41 52 53 56 62 73 79 81 82 84 91 99
Пояснение к коду: Действительно, написанный изначально код некорректен, но его нетрудно исправить, примерно понимая, чего хотел автор.
Коротко о том, что делает исходный код на C++
Генерирует вектор (массив) случайных целых чисел заданного размера (20 штук) в диапазоне от 0 до 100. Выводит сгенерированный массив. Сортирует этот массив с помощью алгоритма слияния (merge sort). Выводит отсортированный массив.
Этот код использует эмодзи в качестве макросов, чтобы «зашифровать» обычные конструкции C++. Вот подробное описание того, что происходит:
Макросы с эмодзи: Определяются макросы, которые заменяют стандартные типы и функции. Например,
😃 означает int
🎉 означает void
🍎 означает std::vector
🖨️ означает std::cout
🔢 означает вызов rand() и т.д.
Функция слияния (🎭): Эта функция принимает вектор и три индекса:
🍌 — начало подмассива
🍍 — конец первого отсортированного сегмента
🍇 — конец второго отсортированного сегмента
Функция копирует левую и правую части в временные векторы, затем сливает их, записывая отсортированные элементы обратно в исходный вектор. Это стандартный шаг алгоритма сортировки слиянием.
Рекурсивная функция сортировки (🍿): Эта функция реализует сортировку слиянием. Если диапазон содержит более одного элемента (условие: 🍌 < 🍇), то:
Находит средний индекс
Рекурсивно сортирует левую и правую половины
Затем объединяет их, вызывая функцию слияния (🎭)
Функция генерации случайного вектора (🎲): Принимает три параметра:
Количество элементов (🎯)
Минимальное значение (🎰)
Максимальное значение (🎳)
Функция создаёт вектор нужного размера и заполняет его случайными числами в заданном диапазоне.
Функция вывода вектора (🖨️): Принимает константную ссылку на вектор и выводит его элементы через пробел с последующим переводом строки.
Функция main (🎯):
Сначала происходит инициализация генератора случайных чисел с помощью srand(time(0)).
Задаются параметры: размер вектора (20 элементов), диапазон случайных чисел от 0 до 100.
Генерируется случайный вектор, который выводится в консоль (неотсортированный вариант).
Затем вектор сортируется с помощью рекурсивной сортировки слиянием (🍿).
После сортировки результат снова выводится в консоль.
Вывод: Код генерирует вектор из 20 случайных чисел от 0 до 100, выводит его, сортирует с помощью алгоритма сортировки слиянием и затем выводит отсортированный вектор.
Задача 2.¶
При использовании ИИ в этой задаче необходимо прислать файл task_2_ai_promt.txt с копией вашего диалога с ИИ-инструментом
В этой задаче вам предстоит поработать с библиотекой Plotly. Это полезный инструмент для создания привлекательных и удобных графиков, который вы наверняка будете использовать в своей работе. Мы рекомендуем заранее изучить его функции и возможности, чтобы вы могли уверенно использовать его в дальнейшем.
Одна из главных особенностей этой библиотеки — возможность настраивать и украшать графики по своему вкусу. Именно этим мы и предлагаем вам заняться в рамках данной задачи.
Ниже приведен код для построения 3D-графика функции с помощью Plotly.
import plotly.graph_objects as go
def plot_3d_surface():
x = np.linspace(-10, 10, 200)
y = np.linspace(-10, 10, 200)
x, y = np.meshgrid(x, y)
z = np.cos(np.sqrt(x ** 2 + y ** 2))
fig = go.Figure(data=[go.Surface(z=z, x=x, y=y)])
fig.update_layout(
title='Cosine Function 3D Surface Plot',
scene=dict(
xaxis_title='X Axis',
yaxis_title='Y Axis',
zaxis_title='Z Axis',
aspectmode='cube',
),
margin=dict(l=0, r=0, b=0, t=0)
)
fig.show()
plot_3d_surface()
Выполните следующие действия в ячейках ниже.
- Задокументируйте функцию и добавьте комментариев. Необходимо это сделать так, чтобы любой человек мог понять, какая у нее задача и что делают отдельные части кода.
import numpy as np
import plotly.graph_objects as go
def plot_3d_surface(**kwargs):
"""
Строит 3D-график поверхности функции z = cos(sqrt(x^2 + y^2)) с помощью Plotly.
Параметры:
----------
**kwargs : dict
Словарь произвольных именованных аргументов, которые позволяют
настраивать как сами данные для графика, так и параметры макета (layout).
Ниже несколько примеров ключей и их значений:
- surface_params : dict
Дополнительные параметры для go.Surface (например, colorscale, opacity и т.д.).
Пример: surface_params={'colorscale': 'Viridis', 'showscale': False}
- layout_params : dict
Дополнительные параметры для layout (например, ширина/высота, фоновый цвет и т.д.).
Пример: layout_params={'width': 700, 'height': 700}
- title : str
Заголовок графика (по умолчанию 'Cosine Function 3D Surface Plot').
- scene : dict
Настройки 3D-сцены (ось X, Y, Z, режим отображения и т.д.).
Пример: scene={'xaxis_title': 'My X', 'yaxis_title': 'My Y', 'zaxis_title': 'My Z'}
- margin : dict
Отступы от краёв фигуры. По умолчанию равны 0.
Пример: margin={'l': 10, 'r': 10, 'b': 20, 't': 40}
Пример вызова:
-------------
plot_3d_surface(
title="Мой 3D-график",
surface_params={'colorscale': [[0, 'pink'], [1, 'lightblue']]},
scene={'xaxis_title': 'X', 'yaxis_title': 'Y', 'zaxis_title': 'Z'},
margin={'l': 0, 'r': 0, 'b': 0, 't': 50},
layout_params={'width': 800, 'height': 800}
)
Результат:
----------
Отрисовывает интерактивный 3D-график, который автоматически открывается в браузере
или внутри Jupyter Notebook, в зависимости от среды выполнения.
"""
# 1) Генерируем координаты x и y в диапазоне [-10, 10] с 200 точками в каждом направлении
x_vals = np.linspace(-10, 10, 200)
y_vals = np.linspace(-10, 10, 200)
# 2) Создаем сетку из x_vals и y_vals
# meshgrid(x, y) возвращает двумерные массивы X и Y, где:
# - X[i, j] = x[j] (по столбцам)
# - Y[i, j] = y[i] (по строкам)
# Это нужно, чтобы можно было рассчитать Z для каждой точки (x, y).
x, y = np.meshgrid(x_vals, y_vals)
# 3) Вычисляем z = cos( sqrt(x^2 + y^2) )
# sqrt(x^2 + y^2) — это радиальное расстояние от начала координат в 2D.
z = np.cos(np.sqrt(x ** 2 + y ** 2))
# Извлекаем пользовательские настройки из kwargs или подставляем значения по умолчанию
surface_params = kwargs.get('surface_params', {})
layout_params = kwargs.get('layout_params', {})
title = kwargs.get('title', 'Cosine Function 3D Surface Plot')
scene = kwargs.get('scene', dict(
xaxis_title='X Axis',
yaxis_title='Y Axis',
zaxis_title='Z Axis',
aspectmode='cube',
))
margin = kwargs.get('margin', dict(l=0, r=0, b=0, t=0))
# 4) Создаем фигуру (Figure) с одним объектом Surface
# go.Figure(...) — это базовый объект графика в Plotly, содержащий набор данных (data) и макет (layout).
# go.Surface(...) — это 3D-поверхность, построенная по массивам x, y, z.
# 'colorscale' — параметр, который отвечает за цветовую палитру.
# Ниже установлен кастомный градиент от светло-розового к светло-голубому,
# если пользователь не передал свою палитру в surface_params.
default_colorscale = [[0, 'rgb(255, 182, 193)'], # светло-розовый
[1, 'rgb(173, 216, 230)']] # светло-голубой
if 'colorscale' not in surface_params:
surface_params['colorscale'] = default_colorscale
fig = go.Figure(
data=[
go.Surface(
x=x, # Координаты X
y=y, # Координаты Y
z=z, # Координаты Z (высота)
**surface_params # Применяем все параметры, которые передал пользователь
)
]
)
# 5) Настраиваем макет (layout) с помощью update_layout
# update_layout позволяет изменить заголовок, размеры, отступы, оси и т.д.
# scene — словарь, содержащий настройки 3D-сцены (названия осей, режимы отображения).
# margin — отступы от краёв графика.
fig.update_layout(
title=title,
scene=scene,
margin=margin,
**layout_params # Применяем все дополнительные параметры для layout, переданные пользователем
)
# 6) Показываем итоговый график
fig.show()
# ------------------------------------------------------------------------------
# Ниже — краткие описания упомянутых функций и опций
# ------------------------------------------------------------------------------
"""
1) np.meshgrid(x, y)
Функция из библиотеки NumPy, которая принимает одномерные массивы x и y
и возвращает двумерные массивы X и Y.
X и Y можно рассматривать как координатную сетку,
где X[i,j] соответствует координате x, а Y[i,j] — координате y.
2) go.Figure
Класс из plotly.graph_objects. Создает объект «фигуры»,
в котором хранятся данные (data) и макет (layout) графика.
3) go.Surface
Класс, описывающий 3D-поверхность в Plotly.
Принимает массивы x, y, z, а также другие параметры (colorscale, opacity и т.д.).
Используется для отображения 3D-поверхностей.
4) update_layout
Метод объекта Figure, который позволяет обновлять параметры макета (layout):
заголовки, легенды, размеры, шрифты, цвета фона, отступы и многое другое.
5) scene
Ключ в layout, отвечающий за 3D-сцену. В нем можно настраивать:
- xaxis_title, yaxis_title, zaxis_title — подписи к осям
- aspectmode (например, 'cube') — режим пропорций осей
- другие параметры (цвет осей, сетку и т.д.)
6) margin
Ключ в layout, отвечающий за отступы графика (слева, справа, сверху, снизу).
margin=dict(l=0, r=0, t=0, b=0) означает, что у графика нет отступов.
"""
# ------------------------------------------------------------------------------
# Пример использования функции plot_3d_surface
# ------------------------------------------------------------------------------
plot_3d_surface(
title="Моя косинусная поверхность",
surface_params={
'colorscale': [[0, 'yellow'], [1, 'lightblue']], # свой градиент
'showscale': True, # показать цветовую шкалу
},
scene={
'xaxis_title': 'X',
'yaxis_title': 'Y',
'zaxis_title': 'Z',
'aspectmode': 'cube'
},
margin={'l': 10, 'r': 10, 'b': 10, 't': 30},
layout_params={
'width': 800,
'height': 800
}
)
- Напишите, что делают следующие функции и опции:
meshgridFigureSurfaceupdate_layoutscenemargin
Вот краткое описание каждой из упомянутых функций и опций:
meshgrid Функция из библиотеки NumPy, которая принимает одномерные массивы координат (например, для осей x и y) и создаёт из них двумерные массивы, представляющие координатную сетку. Это удобно для вычисления значений функции по всей плоскости.
Figure Основной объект в Plotly, представляющий график. Он содержит данные (data) — наборы графических объектов (например, линии, поверхности) — и настройки макета (layout), такие как заголовки, оси, отступы и др.
Surface Класс из Plotly, предназначенный для создания 3D-поверхностей. Он принимает массивы координат x, y, z и другие параметры (например, colorscale) для визуализации функции в трёх измерениях.
update_layout Метод объекта Figure, позволяющий обновлять и настраивать внешний вид графика. С его помощью можно изменить заголовок, размеры, отступы, параметры осей и другие элементы макета.
scene Опция в настройках макета (layout) Plotly, отвечающая за параметры 3D-сцены. Здесь задаются подписи осей (xaxis_title, yaxis_title, zaxis_title), режимы отображения (например, aspectmode) и другие свойства, влияющие на внешний вид 3D-графика.
margin Параметр в layout, который определяет отступы графика от краёв контейнера. Обычно задаётся в виде словаря с ключами l, r, t, b (отступы слева, справа, сверху и снизу соответственно) для точной настройки расположения графика.
Что делает функция ?¶
Генерирует одномерные массивы значений для осей X и Y в диапазоне от –10 до 10, состоящие из 200 точек каждый.
Создаёт двумерную сетку координат (X и Y) на основе этих массивов для дальнейшего расчёта значений Z.
Вычисляет значения Z для каждой точки сетки по формуле: 𝑧¶
cos ( 𝑥 2 + 𝑦 2) z=cos( x 2 +y 2
).
Извлекает настройки от пользователя, позволяющие задать параметры отображения поверхности, макета графика, заголовок, параметры 3D-сцены и отступы.
Если пользователь не указал цветовую схему, устанавливает дефолтный градиент от светло-розового к светло-голубому.
Создаёт интерактивный 3D-график поверхности с помощью Plotly, используя вычисленные данные и заданные параметры.
Обновляет макет графика согласно переданным настройкам (например, заголовок, размеры, отступы, подписи осей).
Отображает итоговый интерактивный 3D-график, который можно вращать, масштабировать и исследовать.
- Выясните, какой параметр отвечает за настройку цветовой палитры графика? Измените код так, чтобы палитра была от светло-розового до светло-голубого.
colorscale = [[0, 'rgb(255,182,193)'], # светло-розовый
[1, 'rgb(173,216,230)']] # светло-голубой
import numpy as np
import plotly.graph_objects as go
def plot_3d_surface():
# Создаем массивы координат x и y в диапазоне от -10 до 10 с 200 точками
x_vals = np.linspace(-10, 10, 200)
y_vals = np.linspace(-10, 10, 200)
x, y = np.meshgrid(x_vals, y_vals)
# Вычисляем z = cos(sqrt(x^2 + y^2))
z = np.cos(np.sqrt(x ** 2 + y ** 2))
# Задаем colorscale от светло-розового до светло-голубого
custom_colorscale = [[0, 'rgb(255,182,193)'], # светло-розовый
[1, 'rgb(173,216,230)']] # светло-голубой
# Создаем 3D-фигура с поверхностью и устанавливаем colorscale
fig = go.Figure(data=[go.Surface(z=z, x=x, y=y, colorscale=custom_colorscale)])
# Обновляем макет графика
fig.update_layout(
title='Cosine Function 3D Surface Plot',
scene=dict(
xaxis_title='X Axis',
yaxis_title='Y Axis',
zaxis_title='Z Axis',
aspectmode='cube',
),
margin=dict(l=0, r=0, b=0, t=0)
)
# Отображаем график
fig.show()
plot_3d_surface()
- Измените функцию так, чтобы она могла получать любые параметры настройки графиков через
kwargs. Это позволит пользователям гибко настраивать параметры графика.
...И напишите подробную инструкцию для пользователя этой функции😃
import numpy as np
import plotly.graph_objects as go
def plot_3d_surface(**kwargs):
"""
Строит интерактивный 3D-график поверхности функции z = cos(sqrt(x^2 + y^2)) с помощью Plotly.
Параметры:
----------
**kwargs : dict
Дополнительные именованные параметры для настройки графика. Возможные ключи:
- surface_params : dict
Словарь параметров для объекта go.Surface.
Например:
{'colorscale': [[0, 'pink'], [1, 'lightblue']], 'showscale': True}
Если не указан, используется градиент от светло-розового до светло-голубого.
- layout_params : dict
Словарь дополнительных параметров для макета графика (layout).
Например:
{'width': 800, 'height': 800}
- title : str
Заголовок графика. По умолчанию: "Cosine Function 3D Surface Plot".
- scene : dict
Словарь параметров 3D-сцены, задающих подписи осей и режим отображения.
Например:
{'xaxis_title': 'X Axis', 'yaxis_title': 'Y Axis', 'zaxis_title': 'Z Axis', 'aspectmode': 'cube'}
- margin : dict
Словарь отступов графика от краёв контейнера.
Например:
{'l': 10, 'r': 10, 'b': 10, 't': 50}
Пример использования:
---------------------
plot_3d_surface(
title="Мой 3D-график косинуса",
surface_params={'colorscale': [[0, 'pink'], [1, 'lightblue']], 'showscale': True},
scene={'xaxis_title': 'X', 'yaxis_title': 'Y', 'zaxis_title': 'Z', 'aspectmode': 'cube'},
margin={'l': 10, 'r': 10, 'b': 10, 't': 50},
layout_params={'width': 800, 'height': 800}
)
Результат:
---------
Функция создаёт и отображает интерактивный 3D-график, который можно вращать, масштабировать и исследовать.
"""
# Генерируем одномерные массивы значений для осей x и y
x_vals = np.linspace(-10, 10, 200)
y_vals = np.linspace(-10, 10, 200)
# Функция np.meshgrid создает двумерную сетку координат из x_vals и y_vals.
# x и y — двумерные массивы, содержащие координаты точек на плоскости.
x, y = np.meshgrid(x_vals, y_vals)
# Вычисляем значение функции z = cos(sqrt(x^2 + y^2))
z = np.cos(np.sqrt(x ** 2 + y ** 2))
# Значения по умолчанию для параметров
default_surface_params = {
'colorscale': [[0, 'rgb(255,182,193)'], [1, 'rgb(173,216,230)']]} # от светло-розового к светло-голубому
default_layout_params = {}
default_title = 'Cosine Function 3D Surface Plot'
default_scene = {
'xaxis_title': 'X Axis',
'yaxis_title': 'Y Axis',
'zaxis_title': 'Z Axis',
'aspectmode': 'cube'
}
default_margin = {'l': 0, 'r': 0, 'b': 0, 't': 0}
# Извлекаем параметры из kwargs, если они заданы пользователем, иначе используем значения по умолчанию
surface_params = kwargs.get('surface_params', default_surface_params)
layout_params = kwargs.get('layout_params', default_layout_params)
title = kwargs.get('title', default_title)
scene = kwargs.get('scene', default_scene)
margin = kwargs.get('margin', default_margin)
# Создаем фигуру (Figure) с объектом Surface.
# go.Surface отвечает за построение 3D-поверхности, а параметр colorscale задает цветовую палитру.
fig = go.Figure(
data=[go.Surface(x=x, y=y, z=z, **surface_params)]
)
# update_layout настраивает макет (layout) графика: заголовок, отступы, настройки 3D-сцены и другие параметры.
fig.update_layout(
title=title,
scene=scene,
margin=margin,
**layout_params
)
# Отображаем график
fig.show()
# Пример использования функции с настройками через kwargs
if __name__ == '__main__':
plot_3d_surface(
title="Мой кастомный 3D-график",
surface_params={'colorscale': [[0, 'pink'], [1, 'lightblue']], 'showscale': True},
scene={'xaxis_title': 'Ось X', 'yaxis_title': 'Ось Y', 'zaxis_title': 'Ось Z', 'aspectmode': 'cube'},
margin={'l': 10, 'r': 10, 'b': 10, 't': 50},
layout_params={'width': 800, 'height': 800}
)
Инструкция: (да, я добавил один и тот же текст в два разных блока, потому что не знал, где именно предполагается ошибка)
Подробная инструкция для пользователя функции Установка зависимостей Убедитесь, что у вас установлены библиотеки:
NumPy: для вычислений и работы с массивами Plotly: для создания интерактивных графиков Если их нет, установите с помощью команды:
bash Копировать pip install numpy plotly Импорт модулей В начале вашего скрипта или Jupyter Notebook импортируйте необходимые модули:
python Копировать import numpy as np import plotly.graph_objects as go Копирование функции Скопируйте приведённую выше функцию plot_3d_surface в ваш проект.
Вызов функции Вызовите функцию plot_3d_surface() без параметров для использования настроек по умолчанию:
python Копировать plot_3d_surface() Либо передайте нужные параметры для кастомизации:
python Копировать plot_3d_surface( title="Мой кастомный 3D-график", surface_params={'colorscale': [[0, 'pink'], [1, 'lightblue']], 'showscale': True}, scene={'xaxis_title': 'Ось X', 'yaxis_title': 'Ось Y', 'zaxis_title': 'Ось Z', 'aspectmode': 'cube'}, margin={'l': 10, 'r': 10, 'b': 10, 't': 50}, layout_params={'width': 800, 'height': 800} ) Параметры настройки через kwargs Функция принимает следующие ключи в kwargs:
surface_params: передается в объект go.Surface. Позволяет настраивать параметры поверхности, такие как colorscale (цветовая палитра), opacity (непрозрачность) и т.д. layout_params: дополнительные параметры для макета (layout) графика, например, размеры (width, height), фон, шрифты и другие. title: строка с заголовком графика. scene: словарь с настройками 3D-сцены, включая подписи осей и режим пропорций (например, aspectmode). margin: словарь с отступами графика от краёв контейнера (ключи l, r, b, t). Результат После вызова функции откроется интерактивное окно (или отобразится график в Jupyter Notebook), где вы сможете вращать и масштабировать 3D-график.
Таким образом, данная функция обеспечивает гибкость настройки графика, позволяя легко менять его внешний вид без необходимости изменять исходный код построения графика
Что делает функция ?¶
Генерирует одномерные массивы значений для осей X и Y в диапазоне от –10 до 10, состоящие из 200 точек каждый.
Создаёт двумерную сетку координат (X и Y) на основе этих массивов для дальнейшего расчёта значений Z.
Вычисляет значения Z для каждой точки сетки по формуле: 𝑧¶
cos ( 𝑥 2 + 𝑦 2) z=cos( x 2 +y 2
).
Извлекает настройки от пользователя, позволяющие задать параметры отображения поверхности, макета графика, заголовок, параметры 3D-сцены и отступы.
Если пользователь не указал цветовую схему, устанавливает дефолтный градиент от светло-розового к светло-голубому.
Создаёт интерактивный 3D-график поверхности с помощью Plotly, используя вычисленные данные и заданные параметры.
Обновляет макет графика согласно переданным настройкам (например, заголовок, размеры, отступы, подписи осей).
Отображает итоговый интерактивный 3D-график, который можно вращать, масштабировать и исследовать.
Сложная часть¶
Задача 3.¶
Эта задача предназначена для развития навыков работы с библиотекой pandas и простого анализа табличных данных. В ней детально описаны все шаги, которые необходимо выполнить. Если что-то не понятно, спросите ИИ или в чате курса.
Yelp — веб-сайт для поиска на местном рынке услуг, например ресторанов или парикмахерских, с возможностью добавлять и просматривать рейтинги и обзоры этих услуг. Для популярных бизнесов имеются сотни обзоров. Для обозревателей на сайте предусмотрены элементы социальной сети.
![]()
Вам предоставляется следующая информация о компаниях на Yelp:
Файл yelp_business.csv:
business_id— уникальный идентификатор компании;name— имя компании;address,city,state— месторасположении компании;latitude,longitude— географические координаты;categories— категории услуг компании.
Файл yelp_review.csv, содержащий оценки пользователей:
business_id— идентификатор компании, соответствующий файлуyelp_business.csv;stars— поставленная пользователем оценка от 1 до 5.
В целях сокращения объема файла, текстовые отзывы пользователей не были включены.
Оригинальную версию датасета в формате json можно посмотреть по ссылке, но использовать нужно выданные данные.
Что нужно сделать:
- Найти город с наибольшим количеством компаний;
- Для этого города определить районы с наиболее качественными услугами. Пример с несколько другой задачей.
- А также найти рестораны с наилучшими отзывами.
Далее в условии встречаются различные подсказки, как можно выполнить определенные части задачи. Вы можете их игнорировать и делать иным способом для получения того же самого промежуточного результата. Однако, если ваше решение окажется значительно менее эффективным (по скорости, количеству и красоте кода) по сравнению с предполагаемым, баллы могут быть снижены.
1. Город с наибольшим количеством компаний¶
Загрузите данные из файла yelp_business.csv с помощью функции pd.read_csv. Посмотрите на первые несколько строк с помощью метода head.
business = pd.read_csv('yelp_data/yelp_business.csv')
Найдите пять городов, по которым присутствует информация о наибольшем количестве компаний. В таблице должен быть указан город (название) и количество компаний в этом городе.
Подсказка. Для выполнения стоит воспользоваться методами groupby, count, sort_values, head.
grouped = business.groupby(["city"]).count()
grouped.sort_values(by='business_id', ascending=False, inplace=True)
top_cities = list(grouped.head().index)
top_cities
['Las Vegas', 'Phoenix', 'Toronto', 'Charlotte', 'Scottsdale']
Пусть N — город с наибольшим количеством компаний. Оставьте в таблице только записи, соответствующие городу N. Нанесите все эти компании на график, в котором по оси $x$ отметьте долготу, а по оси $y$ — широту.
top_city_businesses = business[business.city == top_cities[0]]
# К этому моменту мы еще не использовали датасет с оценками.
import folium
m = folium.Map(location=[top_city_businesses['latitude'].mean(),
top_city_businesses['longitude'].mean()],
zoom_start=10)
for idx, row in top_city_businesses.iterrows():
if idx % 5 == 0:
folium.CircleMarker(
location=[row['latitude'], row['longitude']],
radius=4,
color='blue',
fill=True,
fill_color='blue',
fill_opacity=0.5,
).add_to(m)
m.save('my_map.html');
Сам город находится в сгустке точек. Есть какие-то компании, которые приписаны к этому городу, но находятся далеко от него. Избавьтесь от них, подобрав некоторые границы значений широты и долготы. Изобразите все компании на новом графике. На этом графике должны выделяться некоторые улицы.
import math
def haversine_distance(lat1, lon1, lat2=top_city_businesses['latitude'].mean(),
lon2=top_city_businesses['longitude'].mean()):
"""
Вычисляет расстояние между двумя точками на поверхности Земли,
заданными широтой и долготой, используя формулу гаверсинусов.
Параметры:
lat1, lon1: широта и долгота первой точки (в градусах)
lat2, lon2: широта и долгота второй точки (в градусах)
Возвращает:
Расстояние между точками в километрах.
"""
lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])
dlat = lat2 - lat1
dlon = lon2 - lon1
a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
c = 2 * math.asin(math.sqrt(a))
R = 6371
return R * c
top_city_businesses = top_city_businesses.copy()
top_city_businesses['dist'] = top_city_businesses.apply(
lambda row: haversine_distance(row['latitude'], row['longitude']),
axis=1
)
quantile = top_city_businesses['dist'].quantile(0.95)
top_city_businesses = top_city_businesses[top_city_businesses['dist'] <= quantile];
Разберитесь с тем, как подгрузить интерактивную карту города в качестве фона графика. Можно пойти двумя следующими способами.
- Изучить материалы по библиотеке Plotly. Возможно, потребуется также заглянуть в документацию.
- Поразбираться с библиотекой Folium. Посмотрите множество примеров, может также помочь статья на русском.
Внимание! Интерактивная карта может не сохраниться в ноутбуке, а если сохранится, то вес ноутбука может превзойти 20 Мб, и его не получится отправить боту. В любом случае необходимо сохранить карту в html и отправить ее отдельным файлом в бот. При этом каждый файл html должен не превышать 20 Мб, присылать файлы в архиве нельзя. Аналогично со всеми следующими интерактивными графиками в этом задании.
# блин, я до этого и так построил интерактивную карту, не дочитав до конца.
m = folium.Map(location=[top_city_businesses['latitude'].mean(), top_city_businesses['longitude'].mean()],
zoom_start=10)
for idx, row in top_city_businesses.iterrows():
if idx % 5 == 0:
folium.CircleMarker(
location=[row['latitude'], row['longitude']],
radius=4,
color='blue',
fill=True,
fill_color='blue',
fill_opacity=0.5
).add_to(m)
m.save('my_map2.html')
Если все получилось, вы молодцы! Далее в этой задаче тоже стройте интерактивные карты. Если нет желания разбираться с интерактивными графиками, можно построить статичные, но баллы будут снижены.
2. Оценки компаний¶
Для выполнения задания нужно посчитать среднюю оценку каждой компании, а также количество выставленных оценок.
Загрузите таблицу оценок yelp_review.csv.
reviews = pd.read_csv("yelp_data/yelp_review.csv");
В подгруженной таблице оценок оставьте только компании города N. Для этого установите значения business_id в качестве индекса у таблицы оценок и воспользуйтесь методом loc.
Подсказка. Чтобы индекс снова сделать полем таблицы, можно воспользоваться методом reset_index.
reviews_indexed = reviews.set_index('business_id')
best_city_reviews = reviews_indexed.loc[reviews_indexed.index.isin(top_city_businesses['business_id'])]
best_city_reviews = best_city_reviews.reset_index()
Теперь посчитайте среднюю оценку каждой компании, а также количество выставленных компании оценок.
Подсказка. Помочь в этом могут функции groupby и aggregate([np.mean, np.size]).
companies_average_stars = best_city_reviews.groupby('business_id').aggregate(['mean', 'size'])
Назовите колонки таблицы красивыми именами, изменив <имя таблицы>.columns, после чего напечатайте несколько строк полученной таблицы. Красивые имена — то, что будет понятно простому человеку при чтении ваших результатов. Как именно их назвать — задача аналитика, то есть в данном случае ваша :)
companies_average_stars.columns = pd.MultiIndex.from_tuples(
[('business_id', 'mean'), ('business_id', 'size'),
('stars', 'mean'), ('stars', 'size')],
names=['business_id', 'statistic']
)
companies_average_stars.columns = companies_average_stars.columns.set_levels(
['avg rating', 'num_reviews'], level=1
)
# companies_average_stars.drop(columns=['Unnamed: 0 mean', 'Unnamed: 0 size'], inplace=True)
companies_average_stars = companies_average_stars.drop(columns=[('business_id', 'avg rating'), ('business_id', 'num_reviews')])
companies_average_stars.columns = ['_'.join(x).replace(' ', '_') for x in companies_average_stars.columns]
companies_average_stars.columns = ['avg_rating', 'reviews_count']
Соедините две полученные ранее таблицы по компаниям города N в одну.
Для этого сначала установите поле business_id в качестве индекса в обеих таблицах с помощью set_index. В одной из них это уже должно было быть сделано.
В полученной таблице должны получится поля latitude, longitude, categories, name, а также две колонки со средней оценкой компаний и количеством оценок, которые вы посчитали выше.
Подсказка. Соединение таблиц можно выполнить с помощью join.
Индексы у этих таблиц одинаковые, так что тип джойна не имеет значения.
best_city_reviews_expanded = pd.merge(top_city_businesses, companies_average_stars, on='business_id')
best_city_reviews_expanded.drop(
columns=['Unnamed: 0', 'neighborhood', 'address', 'postal_code', 'is_open'],
inplace=True)
Изобразите все компании на графике, раскрасив точку в цвет, оттенок которого соответствует средней оценке компании. Прозрачность точки выставляйте не более $0.3$. Если у одной компании несколько филиалов с разными адресами, наносите на график все адреса.
import folium
# Функция преобразования рейтинга в RGB, используя логарифмическую шкалу для плавной градации
def rating_to_rgb(rating):
"""
Преобразует оценку от 1 до 5 в RGB цвет с улучшенной градацией с использованием логарифмической шкалы.
1 -> Красный (rgb(255, 0, 0))
5 -> Зелёный (rgb(0, 255, 0))
"""
norm_rating = np.log(rating) / np.log(5) # Логарифмическая нормализация
cmap = plt.get_cmap('RdYlGn') # Палитра от красного до зелёного
norm = Normalize(vmin=0, vmax=1)
sm = ScalarMappable(cmap=cmap, norm=norm)
rgba = sm.to_rgba(norm_rating) # Получаем цвет в формате RGBA
return f'#{int(rgba[0]*255):02x}{int(rgba[1]*255):02x}{int(rgba[2]*255):02x}'
# Создаём карту с центром, взятым по среднему значению координат компаний
m = folium.Map(location=[top_city_businesses['latitude'].mean(), top_city_businesses['longitude'].mean()],
zoom_start=10)
# Добавляем маркеры с цветовой градацией и всплывающей подсказкой, показывающей рейтинг
for idx, row in best_city_reviews_expanded.iterrows():
if idx % 5 == 0:
folium.CircleMarker(
location=[row['latitude'], row['longitude']],
radius=4,
color=rating_to_rgb(row['avg_rating']),
fill=True,
fill_opacity=0.3,
tooltip=f"Рейтинг: {row['avg_rating']:.2f}"
).add_to(m)
# Сохранение карты
m.save('my_map3_enhanced.html')
Чтобы получить районы города, то есть разделить город на "клетки", округлите значения широты и долготы, подобрав оптимальный размер района.
Подсказка. Например, можно сделать так
np.round(долгота*4, decimals=1)*0.25.
best_city_reviews_expanded['latitude'] = round(best_city_reviews_expanded['latitude'], 2)
best_city_reviews_expanded['longitude'] = round(best_city_reviews_expanded['longitude'], 2)
import folium
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
from matplotlib.cm import ScalarMappable
# Функция преобразования рейтинга в RGB, используя логарифмическую шкалу для плавной градации
def rating_to_rgb(rating):
"""
Преобразует оценку от 1 до 5 в RGB цвет с улучшенной градацией.
1 -> Красный (rgb(255, 0, 0))
5 -> Зелёный (rgb(0, 255, 0))
"""
norm_rating = np.log(rating) / np.log(5) # Логарифмическая нормализация
cmap = plt.get_cmap('RdYlGn') # Палитра от красного до зелёного
norm = Normalize(vmin=0, vmax=1)
sm = ScalarMappable(cmap=cmap, norm=norm)
rgba = sm.to_rgba(norm_rating) # Преобразуем в RGBA
return f'#{int(rgba[0]*255):02x}{int(rgba[1]*255):02x}{int(rgba[2]*255):02x}'
# Создаём карту с центром по средним координатам компаний
m = folium.Map(location=[top_city_businesses['latitude'].mean(), top_city_businesses['longitude'].mean()],
zoom_start=10)
# Добавляем маркеры с улучшенной градацией и всплывающей подсказкой, показывающей рейтинг
for idx, row in best_city_reviews_expanded.iterrows():
if idx % 5 == 0:
folium.CircleMarker(
location=[row['latitude'], row['longitude']],
radius=4,
color=rating_to_rgb(row['avg_rating']),
fill=True,
fill_opacity=0.3,
tooltip=f"Рейтинг: {row['avg_rating']:.2f}"
).add_to(m)
# Сохраняем карту
m.save('my_map4_enhanced.html')
Для получения средней оценки компаний по району постройте сводную таблицу при помощи pd.pivot_table, взяв в качестве индексов и колонок округленные широту и долготу, а в качестве значений — оценки. Аггрегирующей функцией является среднее.
Изобразите полученную таблицу при помощи sns.heatmap.
Замечание. В качестве среднего можно было бы посчитать как общее среднее по всем оценкам всех компаний, так и среднее по компаниям их средних оценок. В чем разница этих подходов? Какой из них необходимо использовать в данном случае?
Ответ:¶
При подсчёте общего среднего по всем оценкам, каждая оценка учитывается индивидуально. Это означает, что компании с большим количеством отзывов будут иметь больший вклад в итоговое значение, и результат будет смещён в сторону компаний с массовым числом оценок.
В противоположность этому, среднее по компаниям их средних оценок сначала вычисляет среднюю оценку для каждой компании, а затем берёт среднее этих значений, давая каждой компании одинаковый вес независимо от количества её отзывов.
В данном задании требуется оценить качество услуг на уровне компаний и, в частности, сравнить районы по качеству услуг. Поэтому правильнее использовать среднее по компаниям их средних оценок, чтобы каждая компания вносила равный вклад в итоговую оценку района, а не зависела от того, сколько отзывов она получила.
pivot = pd.pivot_table(best_city_reviews_expanded, index='latitude', columns='longitude', aggfunc='mean', values='avg_rating')
plt.figure(figsize=(8, 6))
ax = sns.heatmap(
pivot,
cmap='RdYlGn',
vmin=1,
vmax=5,
cbar_kws={'label': 'Средняя оценка'},
linewidths=0.5,
linecolor='gray'
)
ax.set_title('Средние оценки по районам')
ax.set_xlabel('Долгота')
ax.set_ylabel('Широта')
plt.gca().invert_yaxis();
Полученный график имеет ряд недостатков. Во-первых, не очень правильно судить о районе, если в нем мало компаний. Во-вторых, на графике цветовая гамма автоматически подстроилась под минимальное и максимальное значения оценки.
Почему эти недостатки могут быть существенными?
Ответ:
Несбалансированность: Возможно, в районе небольшое количество бизнесов, поэтому оценка может быть искаженной, в сравнении с большим: каждая пятерка или единичка очень сильно влияет на значение небольшого района. Может получиться ситуация, похожая с оценкой игр на метакритике: топ1 игра времен по мнению критиков: теннис на пс2, в котором стоит две пятерки. Автоподстройка: Очевидно, что в оценках будет немного отклонений от среднего: около 3.5. По большей части, заведения получают оценки, близкие к четверке, поэтому, когда счет на сотни отзывов, шкалу лучше отнормировать не по шкале 1-5, потому что десятые доли будут важны. А оценки, равные, или очень близкие к единице, вероятно, можно считать выбросами.
Оставьте районы, в которых имеется информация о не менее $30$ компаний. Постройте новый график районов, использовав параметры vmin и vmax у функции sns.heatmap.
cmp_cnt = pd.pivot_table(best_city_reviews_expanded, index='latitude', columns='longitude', aggfunc='size')
plt.figure(figsize=(10, 8))
data = pivot.where(cmp_cnt >= 30)
ax = sns.heatmap(
data,
cmap='RdYlGn',
vmin=3,
vmax=4,
cbar_kws={'label': 'Средняя оценка'},
linewidths=0.5,
linecolor='gray'
)
ax.set_title('Средние оценки по районам (не менее 30 компаний)')
ax.set_xlabel('Долгота')
ax.set_ylabel('Широта')
plt.gca().invert_yaxis()
plt.show();
Сравните полученный график с предыдущим и сделайте вывод.
Вывод: Хитмапа получается более правильной, если убрать районы с малой выборкой, который могут показывать некорректные результаты, нескореллированные в размерах с остальной выборкой.
3. Рестораны¶
Будем считать компанию рестораном, если в поле categories содержится слово restaurant. Обратите внимание, что в анализе данных часто нет четкого формата данных. Например, данное слово может быть написано как с большой буквы, так и с маленькой; может как разделяться ;, так и не разделяться. При возникновении недопонимания стоит посмотреть данные.
Составьте таблицу, в которой будет информация о всех ресторанах города N, для которых имеется не менее $5$ отзывов. Далее постройте heatmap-график районов, в котором каждому району сопоставьте среднюю оценку по ресторанам этого района. Рассматривайте только те районы, в которых есть не менее $10$ ресторанов, для каждого из которых есть не менее $5$ отзывов.
restaurant_categories = [
"Restaurant", "restaurant",
"Restaraunt", "restoraunt",
"Restorant", "restorant",
"Cafe", "cafe", "Café", "caf'e",
"Bistro", "bistro",
"Eatery", "eatery",
"Diner", "diner",
"Fast Food", "fast food",
"Grill", "grill",
"Pub", "pub"
]
bitmask = top_city_businesses.apply(lambda row: True if any([cat in row['categories'].lower() for cat in restaurant_categories]) else False,
axis=1)
top_city_restaurant = top_city_businesses[bitmask]
bitmask = top_city_restaurant.apply(lambda row: True if (companies_average_stars.loc[row["business_id"], 'reviews_count'] >= 5) else False,
axis=1)
top_city_restaurant = top_city_restaurant[bitmask]
# top_city_restaurant = top_city_restaurant.merge(best_city_reviews_expanded, how='inner', on='business_id')
# top_city_restaurant
top_city_restaurant["latitude"] = round(top_city_restaurant['latitude'], 2)
top_city_restaurant["longitude"] = round(top_city_restaurant['longitude'], 2)
cmp_cnt = pd.pivot_table(top_city_restaurant, index='latitude', columns='longitude', aggfunc='size')
# cmp_cnt
ax = sns.heatmap(pivot[cmp_cnt >= 10], cmap=plt.get_cmap('RdYlGn'))
ax.set_xlabel('Долгота')
ax.set_ylabel('Широта');
Text(47.25, 0.5, 'Широта')
Чем полезны ограничения снизу на количество отзывов для ресторана и количество ресторанов в районе?
Ответ: Как уже было сказано выше, ограничения позволяют отсечь непопулярные места: возможно, новые, возможно, уже не работающие, или созданные по ошибке, когда они могут вносить сильный вклад, если в нашем районе не очень много ресторанов.
Кот Василий из города N очень придирчив к выбору ресторана. Он доверяет только ресторанам с высоким рейтингом, который основывается на большом количестве отзывов. Напечатайте в виде таблицы информацию $10$ ресторанах с самым большим рейтингом в порядке убывания рейтинга. Для каждого из этих ресторанов должно быть не менее $50$ отзывов. По каждому ресторану необходимо вывести следующую информации: название ресторана, средняя оценка, количество отзывов, географические координаты, категории.
# top_city_restaurant = top_city_restaurant.merge(companies_average_stars, on='business_id', how='inner')
top_city_restaurant.sort_values(by=['avg_rating', 'reviews_count'], ascending=False, inplace=True)
top_city_restaurant_verified = top_city_restaurant[top_city_restaurant['reviews_count'] >= 50]
top_city_restaurant_verified[['name', 'avg_rating', 'reviews_count', 'latitude', 'longitude', 'categories']].head(10);
Нанесите на карту все рестораны со средней оценкой не менее $4.7$, которая посчитана по не менее $50$ отзывам. Отдельным цветом отметьте 10 ресторанов, которые вы получили ранее.
import matplotlib.patches as patches
plt.figure(figsize=(12, 8))
pivot = pd.pivot_table(
top_city_restaurant_verified[top_city_restaurant_verified["avg_rating"] >= 4.7],
index='latitude',
columns='longitude',
aggfunc='mean',
values='avg_rating'
)
ax = sns.heatmap(pivot, cmap='coolwarm', annot=True, fmt='.2f', linewidths=0.5)
top10 = top_city_restaurant_verified.head(10)
for _, row in top10.iterrows():
lat = round(row['latitude'], 2)
lon = round(row['longitude'], 2)
try:
lat_idx = pivot.index.get_loc(lat)
lon_idx = pivot.columns.get_loc(lon)
# Выделяем ячейку рамкой, чтобы не перекрывать данные
rect = patches.Rectangle(
(lon_idx, lat_idx), 1, 1,
fill=False, edgecolor='lime', lw=3, linestyle='--'
)
ax.add_patch(rect)
except KeyError:
continue
plt.title('Рестораны с оценкой не менее 4.7')
plt.xlabel('Долгота')
plt.ylabel('Широта')
plt.gca().invert_yaxis()
plt.show()
Охарактеризуйте кота Василия, а также сделайте общий вывод по задаче.
Вывод: Кот Василий отличается редкой разборчивостью и признаёт лишь те заведения, где высокие оценки подтверждены множеством отзывов. Такой строгий подход подчёркивает, насколько важно учитывать как качество оценок (их уровень), так и их количество. В результате комплексного отбора и визуального анализа удалось выделить немногие действительно надёжные рестораны, заслуживающие доверия даже такого придирчивого гурмана.
Анализ Yelp-данных показал, что наиболее объективные результаты достигаются при учёте как уровня оценок, так и их количества. Отсев районов и ресторанов с малым объёмом отзывов позволил сконцентрироваться на действительно популярных и качественных заведениях. Итоговая визуализация даёт наглядное представление о географическом распределении высокорейтинговых мест, что может помочь потенциальным посетителям в выборе, а владельцам — в выявлении точек роста и понимании конкурентной среды.
Вставка из прошлого: В задаче мы постарались корректно (то есть отсеяв выбросы, неинформативные районы) выбрать рестораны и районы с самым лучшим рейтингом. Задача демонстрирует, как с помощью pandas отсеять заведения с недостаточным числом отзывов (менее 50) и объективно оценить качество ресторанов. Фильтрация и сортировка по рейтингу позволяют выявить по-настоящему заслуживающие доверия заведения
Задача 4.¶
При использовании ИИ в этой задаче необходимо прислать файл task_4_ai_promt.txt с копией вашего диалога с ИИ-инструментом
Представьте ситуацию, вы занимаетесь своей научной работой, и вам попалась одна интересная статья. Прежде чем глубоко погружаться в решение авторов вам хотелось бы понять, какую задачу решают авторы, насколько успешно, какие у них данные, какие навыки нужны для понимания. Например, было бы очень не оптимально последовательно глубоко разбирать все статьи, на это нужно потратить очень много времени. Предварительный анализ позволяет понять, какие решения в статьях существуют, выделить для себя наиболее интересные. Благодаря изобретению ИИ вы можете ускорить анализ статьей, быстрее разбираться в научных статьях, которые вам действительно интересны.
Выполните следующие действия
Выберите одну научную статью из списка ниже.
Опишите постановку задачи: 1). на простом языке, чтобы ее могли понять люди без технического образования, 2). на математическом языке. Возможно, некоторые моменты окажутся сложными, тем не менее, попробуйте выделить ключевые элементы, используя ИИ.
Разберите структуру входных данных, которые использует решение авторов. Опишите их тип, например, таблица признаков, изображения с метками, текстовые данные с категориями, а также формат представления. Например, случае метода KNN это матрица признаков размером $n \times d$ и вектор меток размером $n$.
Определите ограничения на данные. Уточните, есть ли требования к их балансу, наличию пропущенных значений, допустимым диапазонам чисел и другим характеристикам.
Выясните, решает ли предложенная модель поставленную задачу по мнению авторов. Обратите внимание на используемые метрики и проанализируйте, насколько они подтверждают успешность решения. Погружаться в работу метрик не требуется, достаточно лишь кратко изложить основные результаты.
Опишите, какими знаниями необходимо обладать для понимания работы модели. Это могут быть основы математики, такие как линейная алгебра и теория вероятностей, а также навыки программирования, например, владение Python и библиотеками машинного обучения.
Примечание.
- Ожидаемый объем работы: 200-300 слов.
- Расчетное время выполнения: 1 час.
Трансформеры и языковые модели¶
Attention is All You Need
Vaswani et al., NeurIPS 2017
arXiv:1706.03762BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
Devlin et al., NAACL 2019
arXiv:1810.04805Language Models are Few-Shot Learners
Brown et al., NeurIPS 2020
arXiv:2005.14165Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer
Raffel et al., JMLR 2020
arXiv:1910.10683Scaling Laws for Neural Language Models
Kaplan et al., NeurIPS 2020
arXiv:2001.08361Llama 2: Open Foundation and Fine-Tuned Chat Models
Meta AI, 2023
arXiv:2307.09288GPT-4 Technical Report
OpenAI, 2023
arXiv:2303.08774Gemini: A Family of Highly Capable Multimodal Models
Google DeepMind, 2023
arXiv:2312.11805
Компьютерное зрение и визуальные модели¶
An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale
Dosovitskiy et al., ICLR 2021
arXiv:2010.11929Learning Transferable Visual Models From Natural Language Supervision
Radford et al., ICML 2021
arXiv:2103.00020Segment Anything
Kirillov et al., Meta AI, 2023
arXiv:2304.02643Scaling Vision Transformers to 22 Billion Parameters
Dehghani et al., Google DeepMind, 2023
arXiv:2302.05442
Self-Supervised and Contrastive Learning¶
A Simple Framework for Contrastive Learning of Visual Representations
Chen et al., CVPR 2020
arXiv:2002.05709Self-Supervised Learning of Pretext-Invariant Representations
Misra and Maaten, CVPR 2020
arXiv:1912.01991Big Self-Supervised Models are Strong Semi-Supervised Learners
Chen et al., NeurIPS 2020
arXiv:2006.10029Unsupervised Learning of Visual Features by Contrasting Cluster Assignments
Caron et al., NeurIPS 2020
arXiv:2006.09882
Обучение с подкреплением¶
Reinforcement Learning with Augmented Data
Laskin et al., NeurIPS 2020
arXiv:2004.14990Neural Architecture Search with Reinforcement Learning
Zoph and Le, ICLR 2017
arXiv:1611.01578
Архитектуры нейросетей и их масштабирование¶
EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks
Tan and Le, ICML 2019
arXiv:1905.11946Emergent Abilities of Large Language Models
Wei et al., 2023
arXiv:2206.07682Scaling Data-Constrained Language Models
Muennighoff et al., 2023
arXiv:2305.16264
Математика, байесовские методы и причинно-следственный анализ¶
Deep Learning for Symbolic Mathematics
Lample and Charton, ICLR 2020
arXiv:1912.01412Causal Inference with a Functional Outcome
Ecker et al., 2023
arXiv:2304.07113General Bayesian Inference for Causal Effects Using Covariate Balancing Procedure
Orihara et al., 2024
arXiv:2404.09414A Deep-Learning Based Bayesian Approach to Seismic Imaging and Uncertainty Quantification
Siahkoohi et al., 2020
arXiv:2001.04567Robust Inference for High-Dimensional Linear Models
Wang et al., 2021
arXiv:2106.07717
Выбранная статья: Attention is All You Need
Vaswani et al., NeurIPS 2017
arXiv:1706.03762
Анализ статьи:
Статья "Attention is All You Need" решает задачу машинного перевода – автоматического преобразования текста с одного языка на другой. Простыми словами, модель принимает на вход предложение на одном языке, анализирует взаимосвязи между его словами и генерирует перевод на другом языке, «смотря» на всё предложение одновременно.
Математически задача формулируется следующим образом: пусть входной текст представляется как последовательность
$$ X = (x_1, x_2, \dots, x_n), $$
а требуемый перевод – как последовательность
$$ Y = (y_1, y_2, \dots, y_m). $$
Модель аппроксимирует условное распределение
$$ P(Y \mid X) = \prod_{t=1}^{m} P\Bigl(y_t \mid x_1, \dots, x_n, y_1, \dots, y_{t-1}\Bigr), $$
и обучение осуществляется через максимизацию лог-правдоподобия, что эквивалентно минимизации кросс-энтропии между предсказанными и истинными переводами.
Входные данные представляют собой параллельные текстовые последовательности – пары предложений, где первое является исходным текстом, а второе – его переводом. Текст разбивается на токены, а затем каждый токен преобразуется в числовой вектор (эмбеддинг) фиксированной размерности, что в совокупности образует матрицу размера $n \times d$. Для повышения качества токенизации применяется метод Byte-Pair Encoding (BPE), который разбивает слова на субсловные единицы и помогает корректно обрабатывать редкие или ранее неизвестные слова.
Ограничения на данные включают требование большого и разнообразного корпуса с единообразной разметкой (без пропусков и "шумных" элементов) и приведение последовательностей к фиксированной длине с использованием padding-токенов.
Эффективность модели подтверждается метриками BLEU и perplexity. Например, Transformer продемонстрировал 28.4 BLEU для пары EN→DE, что превосходит традиционные рекуррентные модели и значительно ускоряет обучение. Например, ConvS2S Ensemble, требуя на два порядка большие мощности, показал 26.36 BLEU (EN→DE), а схожий по затратам в обучении, ConvS2S — 25.16.
Для понимания работы модели необходимы знания линейной алгебры (работа с матрицами и векторами), основы теории вероятностей (условные вероятности, softmax, кросс-энтропия) и навыки программирования на Python с использованием библиотек глубокого обучения, таких как TensorFlow или PyTorch.